package com.paipai.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.paipai.config.AlipayConfig;
import com.paipai.entity.Porder;
import com.paipai.loginInterceptor.LoginInterceptor;
import com.paipai.myannotation.MyLog;
import com.paipai.service.PorderService;
import com.paipai.test.RabbitTest;
import com.paipai.utils.BaseController;
import com.paipai.utils.RequestUtils;
import com.paipai.utils.UserInfo;

@Controller
public class PorderController extends BaseController {
	@Autowired
	private PorderService porderService;

	/**
	 * 查询用户订单
	 * 
	 * @param req
	 * @return
	 */
	@RequestMapping("/findorder")
	@ResponseBody
	@MyLog("查询订单")
	public Object findOrder(Integer pageNum, Integer pageSize, String accountType, HttpServletRequest req) {
		System.out.println("查询订单");
		UserInfo userInfo = LoginInterceptor.getTlocal();

		Integer uid = Integer.parseInt(userInfo.getId().toString());

		HashMap<String, Object> map = porderService.findOrder(pageNum, pageSize, accountType, uid);
		// System.out.println(map);
		return map;

	}

	/**
	 * 支付订单
	 * 
	 * @param id
	 * @return
	 * @throws AlipayApiException
	 * @throws IOException
	 */
	@RequestMapping("/payment")
	@MyLog("发起支付")
	public void paymentOrder(Integer id, HttpServletResponse resp) throws AlipayApiException, IOException {
		// 从token获取用户id
		UserInfo userInfo = LoginInterceptor.getTlocal();

		// 以id查询订单信息
		Porder order = porderService.findOrderById(id);

		// 商户订单号，商户网站订单系统中唯一订单号，必填
		String out_trade_no = order.getOrderId().toString();

		// 获得初始化的AlipayClient
		// 1.支付宝网关 2.appID 3.私钥 4.json 5.字符编码utf-8 6.公钥 7.签名方式
		AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id,
				AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key,
				AlipayConfig.sign_type);

		// 设置请求参数
		AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

		// 这里设置支付后跳转的地址
		alipayRequest.setReturnUrl(AlipayConfig.return_url);
		alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

		// 付款金额，必填
		String total_amount = String.valueOf(order.getProPrice());
		// 订单名称，必填
		String subject = "用户" + userInfo.getUsername() + "拍卖订单";
		// 商品描述，可空
		String body = "用户订购商品个数：" + order.getProName();

		// 该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天（1c-当天的情况下，无论交易何时创建，都在0点关闭）。
		// 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
		String timeout_express = "5m";

		alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\"," + "\"total_amount\":\"" + total_amount
				+ "\"," + "\"subject\":\"" + subject + "\"," + "\"body\":\"" + body + "\"," + "\"timeout_express\":\""
				+ timeout_express + "\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

		// 请求
		String result = alipayClient.pageExecute(alipayRequest).getBody();

		resp.setContentType("text/html;charset=utf-8");
		resp.getWriter().write(result);// 直接将完整的表单html输出到页面
		resp.getWriter().flush();
		resp.getWriter().close();

		// return result;

	}

	/**
	 * 注意同步返回结果是以get请求形式返回的
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@MyLog("支付同步回调")
	@RequestMapping("returnUrl")
	public void return_url(HttpServletRequest request, HttpServletResponse response) throws IOException {
		System.out.println("同步回调");
		Map<String, String> paramsMap = RequestUtils.convertRequestParamsToMap(request);
		try {
			boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key,
					AlipayConfig.charset, AlipayConfig.sign_type);
			if (signVerified) {

				Cookie cookie = new Cookie("out_trade_no", paramsMap.get("out_trade_no"));
				cookie.setDomain(".paipai.com");
				cookie.setPath("/");
				Cookie cookie2 = new Cookie("total_amount", paramsMap.get("total_amount"));
				cookie2.setDomain(".paipai.com");
				cookie2.setPath("/");

				response.addCookie(cookie);
				response.addCookie(cookie2);
				response.sendRedirect("http://www.paipai.com/paysuccess.html");
				System.out.println("支付成功页面");

			} else {
				System.out.println("支付失败");
				response.sendRedirect("http://www.paipai.com/payfail.html");

			}
		} catch (AlipayApiException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 注意异步返回结果通知是以post请求形式返回的
	 * 
	 * @param request
	 * @return
	 */
	@MyLog("支付异步回调")
	@RequestMapping("notifyUrl")
	@ResponseBody
	public String notify_url(HttpServletRequest request) {
		System.out.println("异步回调");
		Map<String, String> paramsMap = RequestUtils.convertRequestParamsToMap(request);

		// 订单是否支付成功状态
		String trade_status = paramsMap.get("trade_status");

		try {
			boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key,
					AlipayConfig.charset, AlipayConfig.sign_type);

			// 无论同步异步都要验证签名
			if (!signVerified) {
				// 签名验证失败不处理业务逻辑
				return "failure";
			}

			// 状态要为支付成功!
			if (!trade_status.equals("TRADE_FINISHED") && !trade_status.equals("TRADE_SUCCESS")) {
				// 支付失败不处理业务逻辑
				return "failure";
			}

			// 验证订单信息
			Porder order = orderCheck(paramsMap);

			// 防止重复通知
			if (order.getStatus().equals("已付款")) {
				System.out.println("防重复生效");
				return "success";
			}
			// 处理自己系统的业务逻辑，如：将支付记录状态改为成功，需要返回一个字符串success告知支付宝服务器
			// 此时的业务处理是否成功跟支付宝已经没有关系了
			System.out.println("通过处理业务");
			// 传送订单id,状态码
			Integer row = porderService.paymentOrder(order.getId(), paramsMap.get("trade_no"));

			return "success";

		} catch (AlipayApiException e) {
			e.printStackTrace();
			return "failure";
		}
	}

	/**
	 * 验证订单
	 * 
	 * @param paramsMap
	 * @throws AlipayApiException
	 */
	private Porder orderCheck(Map<String, String> paramsMap) throws AlipayApiException {
		// 以订单自主生成id查询订单信息

		Porder order = porderService.findOrderByOrderId(paramsMap.get("out_trade_no"));

		// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
		if (order == null) {
			throw new AlipayApiException("订单编号错误错误");
		}

		// 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
		float receipt_amount = Float.parseFloat(paramsMap.get("receipt_amount"));
		if (receipt_amount != order.getProPrice()) {
			throw new AlipayApiException("金额错误");
		}

		// 4、验证app_id是否为该商户本身。
		if (!paramsMap.get("app_id").equals(AlipayConfig.app_id)) {
			throw new AlipayApiException("app_id不一致");
		}

		return order;

	}

	/**
	 * 退款
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/refund")
	@ResponseBody
	public String refund(Integer id) {
		System.out.println("退款接口");
		Porder order = porderService.findOrderById(id);

		// 实例化客户端
		AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id,
				AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key,
				AlipayConfig.sign_type);

		// SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
		AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
		refundModel.setTradeNo(order.getTradeNo());
		refundModel.setRefundAmount(order.getProPrice().toString());
		refundModel.setRefundReason("商品退款");

		// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
		AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
		request.setBizModel(refundModel);

		try {
			AlipayTradeRefundResponse response = alipayClient.execute(request);
			System.out.println(response.getMsg() + "\n");
			System.out.println(response.getBody());
			if (response.isSuccess()) {
				Integer row = porderService.refundOrder(id);
				System.out.println("退款成功");
				return "ok";
			}

			System.out.println("支付宝退款错误");
			return "no";
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("异常!支付宝退款错误！");
			return "no";
		}

	}

	/**
	 * 取消
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/cancel")
	@ResponseBody
	public String cancelOrder(Integer id) {

		Integer row = porderService.cancelOrder(id);

		return row > 0 ? "ok" : "no";

	}

	/**
	 * 卖家发货
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/deliver")
	@ResponseBody
	public String deliverOrder(Integer id) {

		Integer row = porderService.deliverOrder(id);

		return row > 0 ? "ok" : "no";

	}

	/**
	 * 卖家发货
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/affirmSign")
	@ResponseBody
	public String affirmSign(Integer id) {

		Integer row = porderService.affirmSignOrder(id);

		return row > 0 ? "ok" : "no";

	}

	// redis
	@Autowired
	private StringRedisTemplate rt;

	// redis 倒计时key
	private static final String KEY_DATE = "product:endDate";

	/**
	 * 轮询
	 */
	// @Async // 多线程
	// @Scheduled(fixedDelay = 10000) // 定时任务
	public void mytask() {
		System.out.println(Thread.currentThread().getName() + ": 执行了 ");
		BoundZSetOperations<String, String> zSetOps = rt.boundZSetOps(KEY_DATE);

		Set<TypedTuple<String>> range = zSetOps.rangeWithScores(0, -1);
		for (TypedTuple<String> typedTuple : range) {
			// 从redis获取时间戳
			Double score = typedTuple.getScore();
			// 将读出来时间戳转成long类型
			long endTime = Long.parseLong(new BigDecimal(score.toString()).toPlainString());

			// 当前时间戳
			long TimeMillis = System.currentTimeMillis();

			// redis的时间戳
			// 跟当前时间相比 如果过时
			if (endTime - TimeMillis <= 0) {
				// 获得redis的value 即商品id

				System.out.println(typedTuple.getValue() + ":商品到期");
				Boolean flag = null;
				try {
					flag = porderService.pollAuctionEndDate(Integer.parseInt(typedTuple.getValue()));

				} catch (Exception e) {
					e.printStackTrace();
				}
				// 执行成功 删除redis 到期统计表
				if (flag) {
					zSetOps.remove(typedTuple.getValue());
				}
			}
		}

	}

}